/**
 * 维护群批量加人业务
 *
 * 业务实体关系：
 * - BatchApplyConfig          一项加人配置：指定要加人的若干“企业微信”，及要加人的群
 *    - BatchApplyTask         加人配置启用后，会为其中每个企业微信开启一个 task，即加人任务
 *      - BatchApplyRecord     企业微信的加人记录
 */
import { tuple, truthy, Service } from '@gnlab/utils'
import type { Dayjs } from 'dayjs'
import { makeHTTPSAvatar } from '@/lib/https'
import { toISOString } from '@/lib/utils'
import type { Services } from '@/services'
import { API } from '@/services/common/api'
import type { GroupContact } from '@/services/contact-types'
import { makeContactId } from '@/services/contacts'
import { friendlyQywxName } from '@/services/qywxs'

// 添加好友的验证消息中可用的变量
export enum ApplyMessageVar {
  QywxName = 'WxWorkName', // 自己的微信昵称
  MemberName = 'WxName', // 目标好友的名称
  GroupName = 'RoomName', // 群名称
}

export enum BatchApplyStatus {
  Idle = 0,
  Applying = 1,
  Pause = 2,
  Complete = 3,
}

export interface BaseBatchApplyConfig {
  name: string // 名称
  status: BatchApplyStatus // 任务状态
  wxids: string[] // 此配置包含哪些企业微信
  groups: BaseBatchApplyGroup[] // 此配置包含哪些群
  ignoreApplyedByOther: boolean // 是否忽略已被其他企微添加过好友的联系人
  ignoreApplyed: boolean // 是否忽略过去 N 天内发送过好友申请的人
  ignoreApplyedDays: number // 仅 ignoreApplyed 为 true 时有效，忽略多少天内发送过请求的人
  dailyLimit: number // 每天加好友的上限
  applyMessage: string // 加好友时的验证消息
  applyInterval: [number, number] // 两次加人的间隔（是一个区间）
}

export interface BatchApplyConfig extends BaseBatchApplyConfig {
  id: number // ID
  applyedCount: number // 已发送请求数
  passedCount: number // 已通过请求数
}

interface RawBaseBatchApplyConfig {
  Status: BatchApplyStatus
  Name: string
  Rooms: {
    WxId: string
    ConversationIds: string // 此 WxId 下，被选中的群的 id 列表，以逗号分隔
  }[]
  IsOtherWxContact: boolean
  IsApplyed: boolean
  ApplyedDays: number
  PerDayCount: number
  ApplyMessage: string
  MinSleep: number
  MaxSleep: number
}

interface RawBatchApplyConfig extends RawBaseBatchApplyConfig {
  Id: number
  ApplyCount: number
  PassCount: number
}

function formatBatchApplyConfig(raw: RawBatchApplyConfig): BatchApplyConfig {
  const wxids = new Set<string>()
  const groups: BaseBatchApplyGroup[] = []
  raw.Rooms.forEach(rawRoom => {
    const ownerWxid = rawRoom.WxId
    wxids.add(ownerWxid)
    groups.push(
      ...rawRoom.ConversationIds.split(',').map(conversationId => ({
        id: makeContactId(ownerWxid, conversationId),
        ownerWxid,
        conversationId,
      })),
    )
  })

  return {
    id: raw.Id,
    status: raw.Status,
    name: raw.Name,
    wxids: [...wxids],
    groups,
    ignoreApplyedByOther: raw.IsOtherWxContact,
    ignoreApplyed: raw.IsApplyed,
    ignoreApplyedDays: raw.ApplyedDays,
    dailyLimit: raw.PerDayCount,
    applyMessage: raw.ApplyMessage,
    applyedCount: raw.ApplyCount,
    passedCount: raw.PassCount,
    applyInterval: tuple(raw.MinSleep, raw.MaxSleep),
  }
}

export function makeRawBatchApplyConfig(config: BaseBatchApplyConfig): RawBaseBatchApplyConfig {
  const roomMap = new Map<string, string[]>()
  config.groups.forEach(group => {
    const list = roomMap.get(group.ownerWxid) ?? []
    roomMap.set(group.ownerWxid, [...list, group.conversationId])
  })
  const rooms = [...roomMap].map(([ownerWxid, conversationIds]) => ({
    WxId: ownerWxid,
    ConversationIds: conversationIds.join(','),
  }))

  return {
    Status: config.status,
    Name: config.name,
    Rooms: rooms,
    IsOtherWxContact: config.ignoreApplyedByOther,
    IsApplyed: config.ignoreApplyed,
    ApplyedDays: config.ignoreApplyedDays,
    PerDayCount: config.dailyLimit,
    ApplyMessage: config.applyMessage,
    MinSleep: config.applyInterval[0],
    MaxSleep: config.applyInterval[1],
  }
}

export function getEmptyBatchApplyConfig(): BaseBatchApplyConfig {
  return {
    name: '',
    status: BatchApplyStatus.Idle,
    wxids: [],
    groups: [],
    ignoreApplyedByOther: false,
    ignoreApplyed: false,
    ignoreApplyedDays: 7,
    dailyLimit: 500,
    applyMessage: '',
    applyInterval: [0, 0],
  }
}

// 加人配置涉及的企业微信信息
export interface BatchApplyConfigQywx {
  wxid: string
  username: string
  nickname: string
  corpname: string
  friendlyName: string
  friendlyFullName: string
  avatar: string
  accountName: string
  accountRemark: string
  configId?: number | null // number: 此企业微信被加入了哪个监控配置，为 null 代表还没有被加入配置
}

interface RawBatchApplyConfigQywx {
  NickName: string
  UserName: string
  CorpName: string
  Avatar: string
  UserAccountName: string
  UserAccountRemarkName: string | null
  WxId: string
  ConfigId: number
}

function formatBatchApplyConfigQywx(raw: RawBatchApplyConfigQywx): BatchApplyConfigQywx {
  const base = {
    wxid: raw.WxId,
    username: raw.UserName,
    nickname: raw.NickName,
    corpname: raw.CorpName,
    avatar: makeHTTPSAvatar(raw.Avatar),
    accountName: raw.UserAccountName,
    accountRemark: raw.UserAccountRemarkName ?? '',
    configId: raw.ConfigId === 0 ? null : raw.ConfigId,
  }
  return {
    ...base,
    friendlyName: friendlyQywxName(base, false),
    friendlyFullName: friendlyQywxName(base),
  }
}

// 加人配置涉及的群信息
export type BaseBatchApplyGroup = Pick<GroupContact, 'id' | 'ownerWxid' | 'conversationId'>

export interface BatchApplyGroup extends BaseBatchApplyGroup {
  creatorWxid: GroupContact['creatorWxid']
  name: GroupContact['name']
  isCorpExternal: boolean // 是否是自己公司的“外部群”
  tags: number[] // 此群被打上的标签列表
}

interface RawBatchApplyGroup {
  ConversationId: string
  WxId: string
  CreateUserId: string
  IsMyCorp: boolean
  NickName: string
  LabelIds: number[]
}

function formatRawBatchApplyGroup(raw: RawBatchApplyGroup): BatchApplyGroup {
  return {
    id: makeContactId(raw.WxId, raw.ConversationId),
    conversationId: raw.ConversationId,
    ownerWxid: raw.WxId,
    creatorWxid: raw.CreateUserId,
    name: raw.NickName,
    isCorpExternal: raw.IsMyCorp,
    tags: raw.LabelIds,
  }
}

export enum BatchApplyTaskStatus {
  Normal = 0, // 正常
  Exceed = 1, // 过频停止（企业微信方限制）
  Limited = 2, // 到达设置上限（用户自己的设置）
}

export interface BatchApplyTask {
  id: number // 企业微信加人任务 id
  wxid: string // 企业微信 id
  status: BatchApplyTaskStatus // 任务状态
  applyedCount: number // 发送申请的数量
  passedCount: number // 申请通过的数量
}

interface RawBatchApplyTask {
  Id: number
  WxId: string
  Status: BatchApplyTaskStatus
  ApplyCount: number
  PassCount: number
}

function formatRawBatchApplyTask(raw: RawBatchApplyTask): BatchApplyTask {
  return {
    id: raw.Id,
    status: raw.Status,
    applyedCount: raw.ApplyCount,
    passedCount: raw.PassCount,
    wxid: raw.WxId,
  }
}

// 添加好友状态
export enum ApplyStatus {
  Applyed = 0, // 已申请（尚未通过）
  Passed = 1, // 已通过好友请求
}

export interface BatchApplyRecord {
  groupName: string // 此好友来自哪个群
  applyed: Date // 发起申请时间
  applyPassed: Date | null // 申请通过时间
  contactName: string // 好友昵称
  status: ApplyStatus // 添加状态
}

interface RawBatchApplyRecord {
  ContactUserName: string
  RoomName: string
  CreateTime: string
  PassTime: string | null
  State: ApplyStatus
}

function formatBatchApplyRecord(raw: RawBatchApplyRecord): BatchApplyRecord {
  return {
    groupName: raw.RoomName,
    applyed: new Date(raw.CreateTime),
    applyPassed: truthy(raw.PassTime) ? new Date(raw.PassTime) : null,
    contactName: raw.ContactUserName,
    status: raw.State,
  }
}

export class GroupBatchApply extends Service<Services> {
  /**
   * 获取用于群批量加人配置的企业微信列表
   *
   * 若指定 configId，则返回所有在线企业微信，以及处于这个 config 中但是不在线的企业微信
   * 若不指定 configId，则仅返回所有在线企业微信
   */
  async getQywxListForConfig(configId?: number) {
    return API('/RoomAddExistMember/GetRoomAddExistMemberWxUserList', {
      data: { ConfigId: configId },
      formatter(raw: RawBatchApplyConfigQywx[]) {
        return raw.map(formatBatchApplyConfigQywx)
      },
    })
  }

  /**
   * 获取用于群批量加人的群列表
   */
  async getGroupsForConfig(ownerWxids: string[]) {
    return API('/RoomAddExistMember/GetRoomAddExistMemberRoomList', {
      data: { wxids: ownerWxids },
      formatter(raw: RawBatchApplyGroup[]) {
        return raw.map(formatRawBatchApplyGroup)
      },
    })
  }

  async loadConfigs() {
    return API('/RoomAddExistMember/GetRoomAddExistMemberConfigs', {
      formatter(list: RawBatchApplyConfig[]) {
        return list.map(formatBatchApplyConfig)
      },
    })
  }

  async addOrUpdateConfig(data: BaseBatchApplyConfig & { id?: number }) {
    return API('/RoomAddExistMember/SaveRoomAddExistMemberConfig', {
      data: {
        Id: data.id,
        ...makeRawBatchApplyConfig(data),
      },
      formatter() {},
    })
  }

  async removeConfig(id: number) {
    return API('/RoomAddExistMember/RemoveRoomAddExistMemberConfig', {
      data: { Id: id },
      formatter() {},
    })
  }

  async loadTasks(configId: number, time?: [Dayjs, Dayjs]) {
    return API('/RoomAddExistMember/GetRoomAddExistMemberTasks', {
      data: {
        ConfigId: configId,
        BeginTime: time ? toISOString(time[0]) : undefined,
        EndTime: time ? toISOString(time[1]) : undefined,
      },
      formatter(raw: RawBatchApplyTask[]) {
        return raw.map(formatRawBatchApplyTask)
      },
    })
  }

  async loadApplyRecords(taskId: number) {
    return API('/RoomAddExistMember/GetRoomAddExistMemberRecords', {
      data: { TaskId: taskId },
      formatter(raw: RawBatchApplyRecord[]) {
        return raw.map(formatBatchApplyRecord)
      },
    })
  }
}
